﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace comm
{
    public class PolygonMesh
    {
        enum DirType
        {
            TOP = 0,
            RIGHT = 3,
            BOTTOM = 2,
            LEFT = 1
        }

        class Block
        {
            PolygonMesh _mesh = null;
            int _x = 0, _y = 0;
            Dictionary<DirType, List<int[]>> _dotDic = new Dictionary<DirType, List<int[]>>();

            public Block(PolygonMesh mesh, int x, int y)
            {
                _mesh = mesh;
                _x = x;
                _y = y;

                var size = _mesh._blockSize / _mesh._mSize;
                var dots = new List<int[]>();
                dots.AddRange(MergeDots(comm.MathHelper.Line(0, 0, size - 1, 0), (int)DirType.TOP));
                dots.AddRange(MergeDots(comm.MathHelper.Line(size - 1, size - 1, size - 1, 0), (int)DirType.RIGHT));
                dots.AddRange(MergeDots(comm.MathHelper.Line(0, size - 1, size - 1, size - 1), (int)DirType.BOTTOM, true));
                dots.AddRange(MergeDots(comm.MathHelper.Line(0, 0, 0, size - 1), (int)DirType.LEFT, true));

                var dic = new Dictionary<string, int[]>();
                foreach (var item in dots)
                {
                    int mx = _x * _mesh._blockSize + item[0] * _mesh._mSize,
                        my = _y * _mesh._blockSize + item[1] * _mesh._mSize,
                        rx = comm.MathHelper.RandomInt(0, _mesh._mSize) + mx,
                        ry = comm.MathHelper.RandomInt(0, _mesh._mSize) + my;
                    var key = item[0] + "," + item[1];
                    int[] rdot = new int[] { rx, ry };
                    if (!dic.ContainsKey(key))
                    {
                        dic.Add(key, new int[] { rx, ry });
                    }
                    rdot = dic[key];
                    var type = (DirType)item[2];
                    if (!_dotDic.ContainsKey(type))
                    {
                        _dotDic.Add(type, new List<int[]>());
                    }
                    _dotDic[type].Add(rdot);
                }
            }

            void SetDots(DirType type, List<int[]> dots)
            {
                var dots1 = _dotDic[type];
                for (int i = 0, len = dots1.Count; i < len; i++)
                {
                    dots1[i][0] = dots[len - i - 1][0];
                    dots1[i][1] = dots[len - i - 1][1];
                }
            }
            int[][] MergeDots(int[][] dots, int value, bool fz = false)
            {
                var list = new List<int[]>();
                foreach (var item in dots)
                {
                    list.Add(new int[] { item[0], item[1], value });
                }
                if (fz)
                {
                    list.Reverse();
                }
                return list.ToArray();
            }

            public void Splice(DirType sType, DirType dType)
            {
                var dic = new Dictionary<DirType, int[]>();
                dic.Add(DirType.TOP, new int[] { _x, _y - 1 });
                dic.Add(DirType.LEFT, new int[] { _x - 1, _y });
                dic.Add(DirType.BOTTOM, new int[] { _x, _y + 1 });
                dic.Add(DirType.RIGHT, new int[] { _x + 1, _y });

                var dot = dic[sType];
                var block = _mesh.GetBlock(dot[0], dot[1]);
                if (block != null)
                {
                    SetDots(sType, block._dotDic[dType]);
                }
            }
            public List<int[]> ToDots()
            {
                var dic = new Dictionary<string, int[]>();
                var dots = new List<int[]>();
                foreach (var list in _dotDic.Values)
                {
                    foreach (var item in list)
                    {
                        var key = item[0] + "," + item[1];
                        if (!dic.ContainsKey(key))
                        {
                            dic.Add(key, new int[] { item[0], item[1] });
                        }
                    }
                }
                foreach (var item in dic.Values)
                {
                    dots.Add(item);
                }
                return dots;
            }
        }

        Block[,] _blocks = null;
        int _bw = 0, _bh = 0, _blockSize = 0, _mSize = 0;

        public PolygonMesh(int bw, int bh, int blockSize, int mSize)
        {
            _mSize = mSize;
            _blockSize = blockSize;
            _bw = bw;
            _bh = bh;
            _blocks = new Block[_bw, _bh];

            for (int x = 0; x < _bw; x++)
            {
                for (int y = 0; y < _bh; y++)
                {
                    _blocks[x, y] = new Block(this, x, y);
                }
            }
        }

        Block GetBlock(int x, int y)
        {
            var rect = new Rectangle(0, 0, _bw, _bh);
            if (rect.Contains(x, y))
            {
                return _blocks[x, y];
            }
            return null;
        }

        public void Splice()
        {
            for (int y = 0; y < _bh; y++)
            {
                var r1 = comm.MathHelper.RandomInt(0, 2);
                for (int x = 0; x < _bw; x++)
                {
                    _blocks[x, y].Splice(
                        r1 == 0 ? DirType.RIGHT : DirType.LEFT,
                        r1 == 0 ? DirType.LEFT : DirType.RIGHT);
                }
            }

            var r2 = comm.MathHelper.RandomInt(0, 2);
            for (int x = 0; x < _bw; x++)
            {
                for (int y = 0; y < _bh; y++)
                {
                    _blocks[x, y].Splice(
                        r2 == 0 ? DirType.TOP : DirType.BOTTOM,
                        r2 == 0 ? DirType.BOTTOM : DirType.TOP);
                }
            }
        }
        public List<int[]>[,] ToBlocks()
        {
            var dots = new List<int[]>[_bw, _bh];
            for (int x = 0; x < _bw; x++)
            {
                for (int y = 0; y < _bh; y++)
                {
                    dots[x, y] = GetBlock(x, y).ToDots();
                }
            }
            return dots;
        }
    }
}
